/*******************************************************************************
* Copyright (c) 2008, 2009 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.swt.tools.builders;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import javax.xml.parsers.DocumentBuilderFactory;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaModelMarker;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.compiler.BuildContext;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.compiler.CompilationParticipant;
import org.eclipse.jdt.core.compiler.batch.BatchCompiler;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.swt.tools.Activator;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
public class Check64CompilationParticipant extends CompilationParticipant {
HashSet<String> sources;
static final char[] INT_LONG = "int /*long*/".toCharArray();
static final char[] INT_LONG_ARRAY = "int[] /*long[]*/".toCharArray();
static final char[] FLOAT_DOUBLE = "float /*double*/".toCharArray();
static final char[] FLOAT_DOUBLE_ARRAY = "float[] /*double[]*/".toCharArray();
static final char[] LONG_INT = "long /*int*/".toCharArray();
static final char[] LONG_INT_ARRAY = "long[] /*int[]*/".toCharArray();
static final char[] DOUBLE_FLOAT = "double /*float*/".toCharArray();
static final char[] DOUBLE_FLOAT_ARRAY = "double[] /*float[]*/".toCharArray();
static final String buildDir = "/.build64/";
static final String pluginDir = "/org.eclipse.swt/";
static final String plugin = "org.eclipse.swt";
static final String SOURCE_ID = "JNI";
static final String CHECK_64_ENABLED = Activator.PLUGIN_ID + "CHECK_64_ENABLED";
static String loadFile (String file) {
if (file == null) return null;
try {
FileReader fr = new FileReader(file);
BufferedReader br = new BufferedReader(fr);
StringBuffer str = new StringBuffer();
char[] buffer = new char[1024];
int read;
while ((read = br.read(buffer)) != -1) {
str.append(buffer, 0, read);
}
fr.close();
return str.toString();
} catch (IOException e) {
throw new RuntimeException("File not found:" + file, e);
}
}
void build(IJavaProject project, String root) throws CoreException {
PrintWriter writer = null;
try {
StringBuffer sourcePath = new StringBuffer(), cp = new StringBuffer();
IClasspathEntry[] entries = project.getResolvedClasspath(true);
for (int i = 0; i < entries.length; i++) {
IClasspathEntry entry = entries[i];
String path = entry.getPath().toPortableString();
if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
if (path.startsWith(pluginDir)) {
if (sourcePath.length() > 0) sourcePath.append(File.pathSeparatorChar);
String dir = root + path.substring(pluginDir.length());
sourcePath.append(dir);
}
} else {
if (cp.length() > 0) cp.append(File.pathSeparator);
cp.append(path);
}
}
String bin = root + "/bin";
if (cp.length() > 0) cp.append(File.pathSeparator);
cp.append(bin);
ArrayList<String> args = new ArrayList<String>();
args.addAll(Arrays.asList(new String[]{
"-nowarn",
"-1.5",
// "-verbose",
"-d", bin,
"-cp", cp.toString(),
"-log", root + "/log.xml",
"-sourcepath", sourcePath.toString(),
}));
args.addAll(sources);
writer = new PrintWriter(new BufferedOutputStream(new FileOutputStream(root + "/out.txt")));
BatchCompiler.compile(args.toArray(new String[args.size()]), writer, writer, null);
writer.close();
writer = null;
project.getProject().findMember(new Path(buildDir)).refreshLocal(IResource.DEPTH_INFINITE, null);
} catch (Exception e) {
throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Problem building 64-bit code", e));
} finally {
if (writer != null) writer.close();
}
}
void create(IContainer file) throws CoreException {
if (file.exists()) return;
switch (file.getType()) {
case IResource.FOLDER:
create(file.getParent());
((IFolder)file).create(true, true, null);
}
}
IResource getResourceWithoutErrors(IProject proj, String path, boolean deleteJNI) throws CoreException {
path = path.replaceAll(buildDir, "/");
String projPath = proj.getLocation().toPortableString();
if (path.startsWith(projPath)) {
path = path.substring(projPath.length());
}
IResource resource = proj.findMember(new Path(path));
boolean hasProblems = false;
IMarker[] markers = resource.findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE);
for (int m = 0; m < markers.length; m++) {
IMarker marker = markers[m];
if (SOURCE_ID.equals(marker.getAttribute(IMarker.SOURCE_ID))) {
if (deleteJNI) marker.delete();
} else {
Object severity = marker.getAttribute(IMarker.SEVERITY);
hasProblems |= severity != null && ((Integer)severity).intValue() == IMarker.SEVERITY_ERROR;
}
}
return hasProblems ? null : resource;
}
void createProblem(IResource resource, String message, int start, int end) throws CoreException {
IMarker marker = resource.createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
int severity = IMarker.SEVERITY_ERROR;
marker.setAttributes(
new String[] {IMarker.MESSAGE, IMarker.SEVERITY, IMarker.CHAR_START, IMarker.CHAR_END, IMarker.SOURCE_ID},
new Object[] {"[32/64] " + message, new Integer(severity), new Integer(start), new Integer(end), SOURCE_ID});
}
void createProblems(IJavaProject project, String root) throws CoreException {
try {
InputStream is = new BufferedInputStream(new FileInputStream(root + "/log.xml"));
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(is));
is.close();
IProject proj = project.getProject();
NodeList sources = doc.getDocumentElement().getElementsByTagName("sources");
for (int i = 0; i < sources.getLength(); i++) {
NodeList src = ((Element)sources.item(i)).getElementsByTagName("source");
for (int j = 0; j < src.getLength(); j++) {
Element source = (Element)src.item(j);
String path = source.getAttribute("path").replace('\\', '/');
IResource resource = getResourceWithoutErrors(proj, path, true);
if (resource != null) {
NodeList problems = source.getElementsByTagName("problems");
for (int k = 0; k < problems.getLength(); k++) {
NodeList problem = ((Element)problems.item(k)).getElementsByTagName("problem");
for (int l = 0; l < problem.getLength(); l++) {
Element node = (Element)problem.item(l);
if (resource != null) {
int start = Integer.parseInt(node.getAttribute("charStart"));
int end = Integer.parseInt(node.getAttribute("charEnd"));
String message = ((Element)node.getElementsByTagName("message").item(0)).getAttribute("value");
createProblem(resource, message, start, end);
}
}
}
}
}
}
} catch (Exception e) {
throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Problem creating 64-bit problems", e));
}
}
String resolvePath(String sourcePath, String simpleName) {
String basePath = sourcePath.substring(0, sourcePath.lastIndexOf("/"));
File file = new File(basePath + "/" + simpleName + ".java");
if (file.exists()) {
return file.getAbsolutePath();
}
// System.out.println("failed=" + simpleName + " " + sourcePath);
return null;
}
TypeDeclaration loadType(HashMap<String, TypeDeclaration> cache, String path) {
if (path == null) return null;
Object value = cache.get(path);
if (value != null) return (TypeDeclaration)value;
ASTParser parser = ASTParser.newParser(AST.JLS4);
parser.setSource(loadFile(path).toCharArray());
CompilationUnit unit = (CompilationUnit)parser.createAST(null);
TypeDeclaration type = (TypeDeclaration)unit.types().get(0);
cache.put(path, type);
return type;
}
boolean is64Type(String type) {
return type.equals("int") || type.equals("long") || type.equals("float") || type.equals("double") ||
type.equals("int[]") || type.equals("long[]") || type.equals("float[]") || type.equals("double[]");
}
void createBadOverwrittenMethodProblems(IJavaProject project, String root) throws CoreException {
if (sources == null) return;
IProject proj = project.getProject();
HashMap<String, TypeDeclaration> cache = new HashMap<String, TypeDeclaration>();
for (Iterator<String> iterator = sources.iterator(); iterator.hasNext();) {
String path = iterator.next();
IResource resource = getResourceWithoutErrors(proj, path, false);
if (resource == null) continue;
TypeDeclaration type = loadType(cache, path);
MethodDeclaration[] methods = type.getMethods();
List<TypeDeclaration> superclasses = new ArrayList<TypeDeclaration>();
TypeDeclaration temp = type;
while (true) {
Type supertype = temp.getSuperclassType();
if (supertype == null) break;
TypeDeclaration stype = loadType(cache, resolvePath(path, supertype.toString()));
if (stype == null) break;
superclasses.add(stype);
temp = stype;
}
for (int i = 0; i < methods.length; i++) {
MethodDeclaration method = methods[i];
for (Iterator<TypeDeclaration> iterator2 = superclasses.iterator(); iterator2.hasNext();) {
TypeDeclaration supertype = iterator2.next();
MethodDeclaration[] supermethods = supertype.getMethods();
for (int j = 0; j < supermethods.length; j++) {
MethodDeclaration supermethod = supermethods[j];
if (method.getName().getIdentifier().equals(supermethod.getName().getIdentifier()) && method.parameters().size() == supermethod.parameters().size()) {
List<SingleVariableDeclaration> mParams = method.parameters();
List<SingleVariableDeclaration> sParams = supermethod.parameters();
for (int k=0; k<sParams.size(); k++) {
SingleVariableDeclaration p1 = mParams.get(k);
SingleVariableDeclaration p2 = sParams.get(k);
String r1 = p1.getType().toString();
String r2 = p2.getType().toString();
if (is64Type(r1) && is64Type(r2)) {
if (!r1.equals(r2) && p1.getName().getIdentifier().equals(p2.getName().getIdentifier())) {
String message = "\"" + p1.getName().getIdentifier() + "\" parameter type does not match super declaration";
createProblem(resource, message, p1.getStartPosition(), p1.getStartPosition() + p1.toString().length());
}
}
}
}
}
}
}
}
}
boolean replace(char[] source, char[] src, char[] dest) {
boolean changed = false;
int start = 0;
while (start < source.length) {
int index = CharOperation.indexOf(src, source, true, start);
if (index == -1) break;
changed |= true;
System.arraycopy(dest, 0, source, index, dest.length);
start = index + 1;
}
return changed;
}
boolean replace(char[] source) {
boolean changed = false;
changed |= replace(source, LONG_INT, INT_LONG);
changed |= replace(source, LONG_INT_ARRAY, INT_LONG_ARRAY);
changed |= replace(source, DOUBLE_FLOAT, FLOAT_DOUBLE);
changed |= replace(source, DOUBLE_FLOAT_ARRAY, FLOAT_DOUBLE_ARRAY);
if (!changed) {
changed |= replace(source, INT_LONG, LONG_INT);
changed |= replace(source, INT_LONG_ARRAY, LONG_INT_ARRAY);
changed |= replace(source, FLOAT_DOUBLE, DOUBLE_FLOAT);
changed |= replace(source, FLOAT_DOUBLE_ARRAY, DOUBLE_FLOAT_ARRAY);
}
return changed;
}
public static boolean getEnabled() {
return Activator.getDefault().getPreferenceStore().getBoolean(CHECK_64_ENABLED);
}
public static void setEnabled(boolean enabled) {
Activator.getDefault().getPreferenceStore().setValue(CHECK_64_ENABLED, enabled);
}
boolean is64bit(IJavaProject project) {
try {
IClasspathEntry[] entries = project.getResolvedClasspath(true);
for (int i = 0; i < entries.length; i++) {
IClasspathEntry entry = entries[i];
if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
String path = entry.getPath().toPortableString();
if (path.equals(pluginDir + "Eclipse SWT PI/win32") ||
path.equals(pluginDir + "Eclipse SWT PI/cocoa") ||
path.equals(pluginDir + "Eclipse SWT PI/gtk")
)
{
return true;
}
}
}
} catch (JavaModelException e) {}
return false;
}
@Override
public void buildFinished(IJavaProject project) {
try {
if (sources == null) return;
if (!getEnabled() || !is64bit(project)) return;
// long time = System.currentTimeMillis();
String root = project.getProject().getLocation().toPortableString() + buildDir;
build(project, root);
createProblems(project, root);
createBadOverwrittenMethodProblems(project, root);
sources = null;
// System.out.println("compiling time=" + (System.currentTimeMillis() - time));
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void buildStarting(BuildContext[] files, boolean isBatch) {
if (sources == null) sources = new HashSet<String>();
// long time = System.currentTimeMillis();
for (int i = 0; i < files.length; i++) {
BuildContext context = files[i];
IFile file = context.getFile();
IProject project = file.getProject();
Path path = new Path(buildDir + file.getProjectRelativePath().toPortableString());
IFile newFile = project.getFile(path);
sources.add(newFile.getLocation().toPortableString());
try {
if (newFile.exists()) {
newFile.delete(true, null);
}
create(newFile.getParent());
char[] source = context.getContents();
replace(source);
newFile.create(new ByteArrayInputStream(new String(source).getBytes()), true, null);
} catch (CoreException e) {
e.printStackTrace();
}
}
// System.out.println("copying time=" + (System.currentTimeMillis() - time));
}
@Override
public void cleanStarting(IJavaProject project) {
if (!isActive(project)) return;
sources = null;
IResource resource = project.getProject().findMember(new Path(buildDir));
if (resource != null) {
try {
resource.delete(true, null);
} catch (CoreException e) {
e.printStackTrace();
}
}
}
@Override
public boolean isActive(IJavaProject project) {
if (project.getProject().getName().equals(plugin)) {
return true;
}
return super.isActive(project);
}
}